package com.mlink.ai.chat.ui.viewmodel

import android.os.Bundle
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.cipher.CipherUtils
import com.google.gson.Gson
import com.mlink.ai.chat.AiChatApplication
import com.mlink.ai.chat.assistant.robot.R
import com.mlink.ai.chat.bean.ChatReceiveData
import com.mlink.ai.chat.bean.ChatRequest
import com.mlink.ai.chat.network.Api
import com.mlink.ai.chat.network.bean.request.GetAnswerRequest
import com.mlink.ai.chat.network.bean.response.GetAnswerResponse
import com.mlink.ai.chat.ui.activity.MainActivity
import com.mlink.ai.chat.ui.adapter.ChatItem
import com.mlink.ai.chat.ui.adapter.ChatType
import com.mlink.ai.chat.utils.AnalyticsEvents
import com.mlink.ai.chat.utils.AnalyticsParams
import com.mlink.ai.chat.utils.BitmapUtils
import com.mlink.ai.chat.utils.ChatLimitUtils
import com.mlink.ai.chat.utils.DeviceUtils
import com.mlink.ai.chat.utils.SingleLiveEvent
import com.mlink.ai.chat.utils.logEvent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.sse.EventSource
import okhttp3.sse.EventSourceListener
import okhttp3.sse.EventSources
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit

const val TAG = "zzzz"

class ChatViewModel : ViewModel() {

    // 先暂时不保留状态
    val lastChatItem = SingleLiveEvent<ChatItem>()
    val errorMsgEvent = SingleLiveEvent<String>()
    val chatReceiveData = SingleLiveEvent<String>()
    val isOpenData = SingleLiveEvent<Boolean>()

    @Volatile
    var tryTimes = 0
    var serverTime = "null"

    fun getAnswer(question: String, conversationId: String? = null, errorMsg: String) {
        val isSubscribe = AiChatApplication.app.appViewModel.getSubscribeLiveData().value ?: false
        if (ChatLimitUtils.isUpToLimit() && !isSubscribe) {
            val item = ChatItem(
                content = GetAnswerResponse(answer = errorMsg),
                ChatType.TYPE_LIMITS,
                false
            )
            lastChatItem.value = item
            errorMsgEvent.value =
                AiChatApplication.app.resources.getString(R.string.chat_up_to_limit)
            return
        }
        AnalyticsEvents.AC_CHATAI_QUS_MSG.logEvent(
            Bundle().apply {
                putString(
                    AnalyticsParams.KEY_TYPE,
                    AnalyticsParams.SUCCESS
                )
                putString(
                    AnalyticsParams.KEY_QUS_TYPE,
                    AnalyticsParams.QUS_TYPE_CHAT
                )
            }
        )
        val previewTime = System.currentTimeMillis() / 1000L
        viewModelScope.launch(Dispatchers.IO) {
            runCatching {
                val uid = DeviceUtils.getUid()
                val source = if (conversationId != null) {
                    conversationId + question + uid + CipherUtils.getCipherKeyFromJNI()
                } else {
                    question + uid + CipherUtils.getCipherKeyFromJNI()
                }
                val md5 = CipherUtils.getMD5(source.toByteArray())
                val request = GetAnswerRequest(uid, question, conversationId, md5)
                Api.chatApi.getAnswer(request)
            }.onSuccess {
                val costTime = System.currentTimeMillis() / 1000L - previewTime
                withContext(Dispatchers.Main) {
                    if (it.ret == 200) {
                        serverTime = it.data?.time.toString()
                        AnalyticsEvents.AC_CHATAI_ANS_MSG.logEvent(
                            Bundle().apply {
                                putString(
                                    AnalyticsParams.KEY_TYPE,
                                    AnalyticsParams.SUCCESS
                                )
                                putString(AnalyticsParams.KEY_TIME, costTime.toString())
                                putString(AnalyticsParams.KEY_ERROR_CODE, "null")
                                putString(AnalyticsParams.KEY_SERVER_TIME, serverTime)
                                putString(AnalyticsParams.KEY_UID, DeviceUtils.getUid())
                                putString(
                                    AnalyticsParams.KEY_CONVERSATION_ID,
                                    conversationId ?: "null"
                                )
                                putString(AnalyticsParams.KEY_MSG_ID, it.data?.msgId ?: "null")
                            }
                        )
                        if (!isSubscribe) ChatLimitUtils.sendRequest()
                        val item = ChatItem(it.data, ChatType.TYPE_ANSWER, false)
                        lastChatItem.value = item
                        return@withContext
                    }
                    AnalyticsEvents.AC_CHATAI_ANS_MSG.logEvent(
                        Bundle().apply {
                            putString(
                                AnalyticsParams.KEY_TYPE,
                                AnalyticsParams.FAILED
                            )
                            putString(AnalyticsParams.KEY_TIME, costTime.toString())
                            putString(AnalyticsParams.KEY_ERROR_CODE, it.ret.toString())
                            putString(AnalyticsParams.KEY_SERVER_TIME, serverTime)
                            putString(AnalyticsParams.KEY_UID, DeviceUtils.getUid())
                            putString(AnalyticsParams.KEY_CONVERSATION_ID, conversationId ?: "null")
                            putString(AnalyticsParams.KEY_MSG_ID, it.data?.msgId ?: "null")
                        }
                    )
                    val item = ChatItem(
                        content = GetAnswerResponse(answer = errorMsg), ChatType.TYPE_ERROR, false
                    )
                    lastChatItem.value = item
                    errorMsgEvent.value = "Server ErrorCode: ${it.ret}"
                }
            }.onFailure {
                withContext(Dispatchers.Main) {
                    AnalyticsEvents.AC_CHATAI_QUS_MSG.logEvent(
                        Bundle().apply {
                            putString(
                                AnalyticsParams.KEY_TYPE,
                                AnalyticsParams.FAILED
                            )
                            putString(
                                AnalyticsParams.KEY_QUS_TYPE,
                                AnalyticsParams.QUS_TYPE_CHAT
                            )
                        }
                    )
                    val item = ChatItem(
                        content = GetAnswerResponse(answer = errorMsg), ChatType.TYPE_ERROR, false
                    )
                    lastChatItem.value = item
                    it.message?.let { errorMsgEvent.value = it }
                }
            }
        }
    }


    fun getAnswer2(role: String, question: String) {
        val chatRequest = ChatRequest(role, question)
        val chatList = mutableListOf<ChatRequest>()
        chatList.add(chatRequest)
        val mediaType = "application/json".toMediaTypeOrNull()
        val requestBody = Gson().toJson(chatList).toRequestBody(mediaType)
        val url = "http://api.lofty-mount.com:9001/chat_v2"
        val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .build()

        val request = Request.Builder()
            .url(url).post(requestBody)
            .header("Accept", "application/json; q=0.5")
            .addHeader("Accept", "text/event-stream")
            .build()

        EventSources.createFactory(client)
            .newEventSource(request = request, listener = eventSourceListener)
        viewModelScope.launch {
            client.newCall(request).enqueue(responseCallback = object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    Log.e(TAG, "API Call Failure ${e.localizedMessage}")
                }

                override fun onResponse(call: Call, response: Response) {
                    Log.d(TAG, "APi Call Success ${response.body?.contentLength()}")
                }
            })
        }
    }

    fun getAnswer3(){
        isOpenData.postValue(true)
        val text = "五千年的风和雨呀藏了多少梦，我是齐天大圣孙悟空"
        viewModelScope.launch {
            for (i in 0..10){
                chatReceiveData.value = text[i].toString()
                delay(1000)
            }
            isOpenData.postValue(false)
        }

    }


    val eventSourceListener = object : EventSourceListener() {
        override fun onOpen(eventSource: EventSource, response: Response) {
            super.onOpen(eventSource, response)
            Log.d(TAG, "Connection Opened")
            isOpenData.value = true
        }

        override fun onClosed(eventSource: EventSource) {
            super.onClosed(eventSource)
            isOpenData.value = true
            Log.d(TAG, "Connection Closed")
        }

        override fun onEvent(
            eventSource: EventSource,
            id: String?,
            type: String?,
            data: String
        ) {
            super.onEvent(eventSource, id, type, data)
            Log.d(TAG, "On Event Received! Data -: $data")
//            chatReceiveData.value = data
        }

        override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {
            super.onFailure(eventSource, t, response)
//            isOpenData.value = false
            Log.d(TAG, "On Failure -: ${response?.body}")
        }
    }


    fun getAnswerWithPhoto(
        question: String,
        file: File?,
        conversationId: String? = null,
        errorMsg: String
    ) {
        val isSubscribe = AiChatApplication.app.appViewModel.getSubscribeLiveData().value ?: false
        if (ChatLimitUtils.isUpToLimit() && !isSubscribe) {
            val item = ChatItem(
                content = GetAnswerResponse(answer = errorMsg),
                ChatType.TYPE_LIMITS,
                false
            )
            lastChatItem.value = item
            errorMsgEvent.value =
                AiChatApplication.app.resources.getString(R.string.chat_up_to_limit)
            return
        }
        AnalyticsEvents.AC_CHATAI_QUS_MSG.logEvent(
            Bundle().apply {
                putString(
                    AnalyticsParams.KEY_TYPE,
                    AnalyticsParams.SUCCESS
                )
                putString(
                    AnalyticsParams.KEY_QUS_TYPE,
                    AnalyticsParams.QUS_TYPE_PIC
                )
            }
        )
        val previewTime = System.currentTimeMillis() / 1000L
        viewModelScope.launch(Dispatchers.IO) {
            runCatching {
                val uid = DeviceUtils.getUid()
                val source = if (conversationId != null) {
                    conversationId + question + uid + CipherUtils.getCipherKeyFromJNI()
                } else {
                    question + uid + CipherUtils.getCipherKeyFromJNI()
                }
                val md5 = CipherUtils.getMD5(source.toByteArray())
                var part: MultipartBody.Part? = null
                file?.let {
                    BitmapUtils.compressPhoto(file)
                    val fileRQ = RequestBody.create("image/*".toMediaTypeOrNull(), file)
                    part = MultipartBody.Part.createFormData("files", file.name, fileRQ)
                }
                val map = hashMapOf<String, RequestBody>()
                map["uid"] = RequestBody.create("text/plain".toMediaTypeOrNull(), uid)
                map["msg"] = RequestBody.create("text/plain".toMediaTypeOrNull(), question)
                map["md5"] = RequestBody.create("text/plain".toMediaTypeOrNull(), md5)
                if (conversationId != null) {
                    map["conversion_id"] =
                        RequestBody.create("text/plain".toMediaTypeOrNull(), conversationId)
                }

                val withoutPhotoMap = hashMapOf<String, Any>().apply {
                    put("uid", uid)
                    put("msg", question)
                    put("md5", md5)
                }
                if (conversationId != null) withoutPhotoMap.put("conversation_id", conversationId)
                if (file != null) {
                    Api.chatApi.uploadImageCaption(map, part)
                } else {
                    Api.chatApi.uploadImageCaptionWithoutPhoto(withoutPhotoMap)
                }
            }.onSuccess {
                if (it.ret == 200) {
                    val data = it.data ?: return@onSuccess
                    getImageCaptionResult(
                        data.msgId ?: "",
                        data.time ?: 0,
                        errorMsg,
                        data.conversationId,
                        previewTime
                    )
                    return@onSuccess
                }
                withContext(Dispatchers.Main) {
                    val item = ChatItem(
                        content = GetAnswerResponse(answer = errorMsg), ChatType.TYPE_ERROR, false
                    )
                    lastChatItem.value = item
                    it.msg?.let { msg -> errorMsgEvent.value = msg }
                }
            }.onFailure {
                withContext(Dispatchers.Main) {
                    AnalyticsEvents.AC_CHATAI_QUS_MSG.logEvent(
                        Bundle().apply {
                            putString(
                                AnalyticsParams.KEY_TYPE,
                                AnalyticsParams.FAILED
                            )
                            putString(
                                AnalyticsParams.KEY_QUS_TYPE,
                                AnalyticsParams.QUS_TYPE_PIC
                            )
                        }
                    )
                    val item = ChatItem(
                        content = GetAnswerResponse(answer = errorMsg), ChatType.TYPE_ERROR, false
                    )
                    lastChatItem.value = item
                    it.message?.let { errorMsgEvent.value = it }
                }
            }
        }
    }


    private suspend fun getImageCaptionResult(
        msgId: String,
        lastTime: Int,
        errorMsg: String,
        conversationId: String?,
        previewTime: Long
    ) {
        val isSubscribe = AiChatApplication.app.appViewModel.getSubscribeLiveData().value ?: false
        if (tryTimes > 10) {
            tryTimes = 0
            withContext(Dispatchers.Main) {
                val item = ChatItem(
                    content = GetAnswerResponse(answer = errorMsg), ChatType.TYPE_ERROR, false
                )
                lastChatItem.value = item
                errorMsgEvent.value = errorMsg
            }
            return
        }
        runCatching {
            tryTimes++
            val uid = DeviceUtils.getUid()
            val source = lastTime.toString() + msgId + uid + CipherUtils.getCipherKeyFromJNI()
            val md5 = CipherUtils.getMD5(source.toByteArray())
            val map = hashMapOf<String, Any>().apply {
                put("uid", uid)
                put("msg_id", msgId)
                put("last_time", lastTime)
                put("md5", md5)
            }
            Api.chatApi.getImageCaptionResult(map)
        }.onSuccess {
            if (it.ret == 200) {
                withContext(Dispatchers.Main) {
                    serverTime = it.data?.time.toString()
                    val costTime = System.currentTimeMillis() / 1000L - previewTime
                    AnalyticsEvents.AC_CHATAI_ANS_MSG.logEvent(
                        Bundle().apply {
                            putString(
                                AnalyticsParams.KEY_TYPE,
                                AnalyticsParams.SUCCESS
                            )
                            putString(AnalyticsParams.KEY_TIME, costTime.toString())
                            putString(AnalyticsParams.KEY_ERROR_CODE, "null")
                            putString(AnalyticsParams.KEY_SERVER_TIME, serverTime)
                            putString(AnalyticsParams.KEY_UID, DeviceUtils.getUid())
                            putString(AnalyticsParams.KEY_CONVERSATION_ID, conversationId ?: "null")
                            putString(AnalyticsParams.KEY_MSG_ID, it.data?.msgId ?: "null")
                        }
                    )
                    if (!isSubscribe) ChatLimitUtils.sendRequest()
                    val response = it.data
                    response?.conversationId = conversationId
                    val item = ChatItem(response, ChatType.TYPE_ANSWER, false)
                    lastChatItem.value = item
                    tryTimes = 0
                }
                return@onSuccess
            }
            if (it.ret == 40300) {
                delay(10_000L)
                getImageCaptionResult(
                    msgId,
                    it.data?.time ?: lastTime,
                    errorMsg,
                    conversationId,
                    previewTime
                )
                return@onSuccess
            }
            withContext(Dispatchers.Main) {
                val costTime = System.currentTimeMillis() / 1000L - previewTime
                AnalyticsEvents.AC_CHATAI_ANS_MSG.logEvent(
                    Bundle().apply {
                        putString(
                            AnalyticsParams.KEY_TYPE,
                            AnalyticsParams.FAILED
                        )
                        putString(AnalyticsParams.KEY_TIME, costTime.toString())
                        putString(AnalyticsParams.KEY_ERROR_CODE, it.ret.toString())
                        putString(AnalyticsParams.KEY_SERVER_TIME, serverTime)
                        putString(AnalyticsParams.KEY_UID, DeviceUtils.getUid())
                        putString(AnalyticsParams.KEY_CONVERSATION_ID, conversationId ?: "null")
                        putString(AnalyticsParams.KEY_MSG_ID, it.data?.msgId ?: "null")
                    }
                )
                val item = ChatItem(
                    content = GetAnswerResponse(answer = errorMsg), ChatType.TYPE_ERROR, false
                )
                lastChatItem.value = item
                errorMsgEvent.value = errorMsg
            }
        }.onFailure {
            tryTimes = 0
            withContext(Dispatchers.Main) {
                val item = ChatItem(
                    content = GetAnswerResponse(answer = errorMsg), ChatType.TYPE_ERROR, false
                )
                lastChatItem.value = item
                errorMsgEvent.value = it.message ?: ""
            }
        }
    }
}